All files / local remote_document_change_buffer.ts

100% Statements 28/28
100% Branches 2/2
100% Functions 7/7
100% Lines 26/26
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100                                2x     2x     2x                             2x 1388x   1388x     2x 1103x 1102x                               2x       1204x   1203x 1203x 94x   1109x               1387x 1387x   1386x 1386x 1086x       1386x   1386x       2x 3694x 3691x   2x  
/**
 * Copyright 2017 Google Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
 
import { MaybeDocumentMap, maybeDocumentMap } from '../model/collections';
import { MaybeDocument } from '../model/document';
import { DocumentKey } from '../model/document_key';
import { assert } from '../util/assert';
 
import { PersistenceTransaction } from './persistence';
import { PersistencePromise } from './persistence_promise';
import { RemoteDocumentCache } from './remote_document_cache';
 
/**
 * An in-memory buffer of entries to be written to a RemoteDocumentCache.
 * It can be used to batch up a set of changes to be written to the cache, but
 * additionally supports reading entries back with the `getEntry()` method,
 * falling back to the underlying RemoteDocumentCache if no entry is
 * buffered.
 *
 * NOTE: This class was introduced in iOS to work around a limitation in
 * LevelDB. Given IndexedDb has full transaction support with
 * read-your-own-writes capability, this class is not technically needed, but
 * has been preserved as a convenience and to aid portability.
 */
export class RemoteDocumentChangeBuffer {
  private changes: MaybeDocumentMap | null = maybeDocumentMap();
 
  constructor(private remoteDocumentCache: RemoteDocumentCache) {}
 
  /** Buffers a `RemoteDocumentCache.addEntry()` call. */
  addEntry(maybeDocument: MaybeDocument): void {
    const changes = this.assertChanges();
    this.changes = changes.insert(maybeDocument.key, maybeDocument);
  }
 
  // NOTE: removeEntry() is not presently necessary and so is omitted.
 
  /**
   * Looks up an entry in the cache. The buffered changes will first be checked,
   * and if no buffered change applies, this will forward to
   * `RemoteDocumentCache.getEntry()`.
   *
   * @param transaction The transaction in which to perform any persistence
   *     operations.
   * @param documentKey The key of the entry to look up.
   * @return The cached Document or NoDocument entry, or null if we have nothing
   * cached.
   */
  getEntry(
    transaction: PersistenceTransaction,
    documentKey: DocumentKey
  ): PersistencePromise<MaybeDocument | null> {
    const changes = this.assertChanges();
 
    const bufferedEntry = changes.get(documentKey);
    if (bufferedEntry) {
      return PersistencePromise.resolve(bufferedEntry);
    } else {
      return this.remoteDocumentCache.getEntry(transaction, documentKey);
    }
  }
 
  /**
   * Applies buffered changes to the underlying RemoteDocumentCache, using
   * the provided transaction.
   */
  apply(transaction: PersistenceTransaction): PersistencePromise<void> {
    const changes = this.assertChanges();
 
    const promises: Array<PersistencePromise<void>> = [];
    changes.forEach((key, maybeDoc) => {
      promises.push(this.remoteDocumentCache.addEntry(transaction, maybeDoc));
    });
 
    // We should not be used to buffer any more changes.
    this.changes = null;
 
    return PersistencePromise.waitFor(promises);
  }
 
  /** Helper to assert this.changes is not null and return it. */
  private assertChanges(): MaybeDocumentMap {
    assert(this.changes !== null, 'Changes have already been applied.');
    return this.changes!;
  }
}